using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Datorgrafik_lab2.Components;
using Datorgrafik_lab2.Helpers;
using Datorgrafik_lab2.Components.Input;
using Datorgrafik_lab2.Objects;
using Datorgrafik_lab2.Primitives;

namespace Datorgrafik_lab2 {
    public class Game1 : Microsoft.Xna.Framework.Game {
        private static InputManager _input = null;
        public static InputManager Input
        {
            get { return _input; }
        }

        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        private int windowWidth;
        private int windowHeight;
        private GraphicsDevice device;

        private BasicEffect effect;
        private RasterizerState WIREFRAME_RASTERIZER_STATE = new RasterizerState() { 
            CullMode = CullMode.CullCounterClockwiseFace, FillMode = FillMode.Solid };

        private Camera camera;
        private FlyingCamera fcamera;
        private Debug _debug;

        private Axes axes;
        private SpherePrimitive sp;

        private Ground ground;
        private Water water;
        private Tank tank;

        float sealevel;

        Boolean isPlayerMode;

        public Game1() {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            IsFixedTimeStep = false;
            //graphics.SynchronizeWithVerticalRetrace = false;

            Components.Add(new FPS(this));

            _debug = new Debug(this);
            Components.Add(_debug);
            Services.AddService(typeof(Debug), _debug);

            InputHandler ip = new InputHandler(this);
            Components.Add(ip);
            Services.AddService(typeof(IInputHandler), ip);

            _input = new InputManager(this);
            Components.Add(_input);
            //Services.AddService(typeof(InputManager), _input);

            tank = new Tank(Matrix.Identity);
        }

        protected override void Initialize() {
            // TODO: Add your initialization logic here

            // Setup the window to be a quarter the size of the desktop.
            windowWidth = graphics.GraphicsDevice.DisplayMode.Width / 2;
            windowHeight = graphics.GraphicsDevice.DisplayMode.Height / 2;

            // Setup frame buffer.
            graphics.SynchronizeWithVerticalRetrace = false;
            graphics.PreferredBackBufferWidth = windowWidth;
            graphics.PreferredBackBufferHeight = windowHeight;
            graphics.PreferMultiSampling = true;

            sealevel = 1f;
            isPlayerMode = false;
            _debug.mode = "Camera";

            base.Initialize();
        }

        protected override void LoadContent() {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            device = graphics.GraphicsDevice;
            this.camera = new Camera(device);

            GraphicsDevice.RasterizerState = WIREFRAME_RASTERIZER_STATE;
            effect = new BasicEffect(graphics.GraphicsDevice);
            effect.VertexColorEnabled = true;
            effect.Projection = camera.ViewProjectionMatrix;

            fcamera = new FlyingCamera();
            axes = new Axes(device);
            sp = new SpherePrimitive(device);
            ground = new Ground(device, Vector3.Zero, 1f, Content.Load<Texture2D>("earth_topography"));
            water = new Water(device, Vector3.Zero, 0.96f, Content.Load<Texture2D>("ocean"));
            tank.Load(Content);
        }

        protected override void UnloadContent() {
        }

        protected override void Update(GameTime gameTime) {
            float elapsedTime = gameTime.ElapsedGameTime.Milliseconds;

            //Read input
            _input.Update();
            IInputHandler inputHandler = (IInputHandler)Services.GetService(typeof(IInputHandler));
            inputAction(inputHandler.getUnhandledActions(), elapsedTime);

            //To make the camera mov           
            if(isPlayerMode) {
                camera.Update(tank);
            } else {
                camera.Update(fcamera.Position, fcamera.Rotation);
            }
            water.Update(this.sealevel);
            
            tank.Update(gameTime);
            tank.Position = ground.GetSphereCords(tank.GetPositionOnMap());            

            _debug.cameraPos = camera.Position;
            _debug.playerPos = tank.Position;
            _debug.playerDir = tank.direction;

            base.Update(gameTime);
        }

        private void inputAction(List<ActionType> actions, float elapsedTime) {
            foreach(var action in actions) {
                if(action == ActionType.Quit)
                    this.Exit();
                if(action == ActionType.IncreaseSealevel && sealevel<1f)
                    sealevel += 0.01f;
                if(action == ActionType.DecreaseSealevel && sealevel > -5f)
                    sealevel -= 0.01f;
                if (_input.IsSwitchPressed())
                {
                    if (this.isPlayerMode)
                    {
                        this.isPlayerMode = false;
                        _debug.mode = "Camera";
                    }
                    else
                    {
                        this.isPlayerMode = true;
                        _debug.mode = "Player";
                    }
                }

                if (isPlayerMode)
                {
                    //tank.performAction(action, elapsedTime);
                    tank.HandleInput(_input, elapsedTime);
                }
                else
                {
                    fcamera.PerformAction(action, elapsedTime);
                }
            }
        }

        protected override void Draw(GameTime gameTime) {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            //Switch to 3D rendering since we have drawn the FPS in 2D
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;            
            effect.View = camera.ViewMatrix;
            effect.World = Matrix.Identity;

            GraphicsDevice.RasterizerState = WIREFRAME_RASTERIZER_STATE;

            //axes.draw();
            ground.Draw(effect, Matrix.Identity, true);
            water.Draw(effect, Matrix.Identity, false);            
            tank.Draw(effect.World, effect.View, effect.Projection);

            base.Draw(gameTime);
        }
    }
}